// scaup
// (c) 2010, Normen Müller, normen.mueller@gmail.com  
// $Id: Iterators.scala 125 2010-09-16 13:48:09Z normen.mueller $
package scaup.data

import scaup.PimpedType

/** @author  <a href="mailto:normen.mueller@googlemail.com">Normen M\u00FCller</a>
 */
sealed trait IteratorW[T] extends PimpedType[Iterator[T]] {
  import scaup.Scaup._

  /** Given the partial function <code>f</code>, filter out elements over which <code>f</code> is undefined,
   *  and map the remaining elements with <code>f</code>.
   *  <p><i>Note:</i> This evaluates <code>f</code> twice for each element.</p>
   *  @see scala.collection.TraversableLike#collect
   */
  def filterMap[U](f: PartialFunction[T, U]) = value filter (f.isDefinedAt) map f

  /** Given the function <code>f</code>, filter out elements for which <code>f</code> returns <code>None</code>,
   *  and map the remaining elements with <code>f</code>, extracting the value from the <code>Some</code>.
   *  <p><i>Note</i> This evaluates <code>f</code> once for each element.</p>
   */
  def filterMapOption[U](f: Function[T, Option[U]]) = value map f filter (None !=) map (_.get)

  /** Given the partial predicate <code>p</code>, find and return the first element over which <code>p</code>
   *  is defined, if any.
   */
  def ?(p: PartialFunction[T, Boolean]): Option[T] = value find (p.isDefinedAt)

  /** Given the partial predicate <code>p</code>, apply <code>p</code> over which it is defined and return true,
   *  iff there is at least one element for which <code>p</code> yields <code>true</code>.
   */
  def !(p: PartialFunction[T, Boolean]): Boolean = value exists (p.isDefinedAt)
}

/** @author  <a href="mailto:normen.mueller@googlemail.com">Normen M\u00FCller</a>
 */
trait Iterators {
  implicit def IteratorTo[T](it: Iterator[T]): IteratorW[T] = new IteratorW[T] {
    val value = it
  }

  implicit def javaIterator2scalaIterator[T](i: java.util.Iterator[T]): Iterator[T] = new Iterator[T] {
    def next = i.next
    def hasNext = i.hasNext
  }

  implicit def scalaIterator2javaIterator[T](k: Iterator[T]): java.util.Iterator[T] = new java.util.Iterator[T] {
    def hasNext = k.hasNext
    def next = k.next
    def remove = error("not supported")
  }

  /** Keeps running the block until it returns null.
   */
  def nonNull[T <: AnyRef](f: => T) = new Iterator[T] {
    private var n: T = _
    private var h = false
    def hasNext = {
      if (!h) { n = f; h = true }
      n != null
    }
    def next = {
      if (!h) n = f
      h = false
      n
    }
  }

  /** Keeps running the block until it returns a negative number.
   */
  def nonNegative(f: => Int) = new Iterator[Int] {
    private var n = 0
    private var h = false
    def hasNext = {
      if (!h) { n = f; h = true }
      n >= 0
    }
    def next = {
      if (!h) n = f
      h = false
      n
    }
  }
}
